package menuimpl

import (
	"context"
	"errors"
	"fmt"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
	"go-caipu/pkg/services/admin/menu"
	"go-caipu/pkg/services/admin/model"
	"go-caipu/pkg/services/admin/sysapi"
	"go-caipu/pkg/util"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)
import "go-caipu/pkg/infra/log"

type store interface {
	// GetMenuWithRole 首页登录后查询导航主菜单
	GetMenuWithRole(ctx context.Context, roleIds []string) ([]menu.Menu, error)
	GetMenuWithRoleNew(ctx context.Context, roleIds []string) ([]*menu.MainMenu, error)
	GetMenuList(ctx context.Context) ([]menu.Menu, error)
	GetMenu(ctx context.Context, cmd menu.GetMenuCommand) (menu.Menu, error)
	Insert(ctx context.Context, menu menu.Menu) error
	// BatchInsert 批量插入菜单[插件菜单生成使用]
	BatchInsert(ctx context.Context, cmd []menu.Menu) (err error)
	Update(ctx context.Context, menu menu.Menu) error
	Remove(ctx context.Context, cmd menu.RemoveMenuCommand) error
	UpdateMenuApi(ctx context.Context, cmd menu.UpdateMenuApiCommand) (err error)
}

type sqlStore struct {
	db     *gorm.DB
	logger log.Logger
}

func (m sqlStore) GetMenuList(ctx context.Context) (result []menu.Menu, err error) {
	var items []model.SysMenu
	err = m.db.Model(&model.SysMenu{}).Preload("CreateUser").Preload("UpdateUser").Preload("SysApi").Find(&items).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = errors.New(fmt.Sprintf("menu record not exists"))
		return
	}
	if err != nil {
		m.logger.Log(fmt.Sprintf("get menu list error:%s", err.Error()))
		return
	}
	for _, v := range items {
		result = append(result, menuDtoToMenu(v))
	}
	return
}

func (m sqlStore) GetMenu(ctx context.Context, cmd menu.GetMenuCommand) (menu2 menu.Menu, err error) {
	var model = model.SysMenu{}
	err = m.db.Model(&model).Preload("SysApi").First(&model, cmd.Id).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = errors.New(fmt.Sprintf("menuId:%d record not exists", cmd.Id))
		return
	}
	return menuDtoToMenu(model), nil
}

// Insert 插入菜单
func (m sqlStore) Insert(ctx context.Context, cmd menu.Menu) (err error) {
	tx := m.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	//插入菜单数据
	data := menuToMenuDto(model.SysMenu{}, cmd)
	err = tx.Create(&data).Error
	if err != nil {
		m.logger.Log(fmt.Sprintf("insert Title:%s db  error:%s", cmd.Title, err.Error()))
		return err
	}
	//find all insert api
	var apiList = make([]model.SysApi, 0)
	tx.Where("id in ?", cmd.Apis).Find(&apiList)
	data.SysApi = apiList
	err = tx.Save(&data).Error
	if err != nil {
		m.logger.Log(fmt.Sprintf("insert menu  error:%s", err.Error()))
		return err
	}
	//构造部门级联数据
	err = updateMenuPath(tx, &data)
	return err
}

func (m sqlStore) BatchInsert(ctx context.Context, cmd []menu.Menu) (err error) {
	tx := m.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	menus := make([]model.SysMenu, 0)
	for i := range cmd {
		//menu
		menu := menuToMenuDto(model.SysMenu{}, cmd[i])
		menu.CreatedAt = time.Now()
		menu.UpdatedAt = time.Now()
		menu.CreateBy = cmd[i].CreateBy
		menu.UpdateBy = cmd[i].UpdateBy
		menuApis := make([]model.SysApi, 0)
		tx.Where("id in ?", cmd[i].Apis).Find(&menuApis)
		menu.SysApi = menuApis
		menus = append(menus, menu)
	}
	err = tx.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "id"}},
	}).Create(&menus).Error
	if err != nil {
		m.logger.Log(fmt.Sprintf("batch insert sys_menu  error:%s", err.Error()))
		return err
	}
	if err != nil {
		m.logger.Log(fmt.Sprintf("export  sys_menu  error:%s", err.Error()))
		return err
	}
	return nil
}

// updateMenuPath 重新构造菜单树对象
func updateMenuPath(tx *gorm.DB, menu *model.SysMenu) error {
	deptPath := util.Int64ToString(menu.MenuId) + "/"
	if menu.ParentId != 0 {
		var Parent model.SysMenu
		tx.First(&Parent, menu.ParentId)
		deptPath = Parent.Paths + deptPath
	} else {
		deptPath = "/0/" + deptPath
	}
	if err := tx.Model(&menu).Update("paths", deptPath).Error; err != nil {
		return err
	}
	return nil
}
func (m sqlStore) Update(ctx context.Context, cmd menu.Menu) (err error) {
	tx := m.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var apiList = make([]model.SysApi, 0)
	var model = model.SysMenu{}
	tx.Preload("SysApi").First(&model, cmd.MenuId)
	tx.Where("id in ?", cmd.Apis).Find(&apiList)
	err = tx.Model(&model).Association("SysApi").Delete(model.SysApi)
	if err != nil {
		logger.Errorf("delete policy error:%s", err)
		return err
	}
	model = menuToMenuDto(model, cmd)
	model.UpdatedAt = time.Now()
	model.SysApi = apiList
	err = tx.Save(&model).Error
	if err != nil {
		m.logger.Log(fmt.Sprintf("Update menu   error:%s", err.Error()))
		return err
	}
	//构造部门级联数据
	err = updateMenuPath(tx, &model)
	return err
}
func menuDtoToMenu(model model.SysMenu) menu.Menu {
	result := menu.Menu{
		MenuId:           model.MenuId,
		MenuName:         model.MenuName,
		Title:            model.Title,
		Icon:             model.Icon,
		Path:             model.Path,
		Paths:            model.Paths,
		MenuType:         model.MenuType,
		Status:           model.Status,
		Action:           model.Action,
		Permission:       model.Permission,
		Redirect:         model.Redirect,
		ParentId:         model.ParentId,
		NoCache:          model.NoCache,
		Breadcrumb:       model.Breadcrumb,
		Component:        model.Component,
		Sort:             model.Sort,
		Visible:          model.Visible,
		IsFrame:          model.IsFrame,
		ControlBy:        model.ControlBy,
		CreatedAt:        model.CreatedAt.Format("2006-01-02 15:04:05"),
		UpdatedAt:        model.UpdatedAt.Format("2006-01-02 15:04:05"),
		CreateUserString: model.CreateUser.NickName,
		UpdateUserString: model.UpdateUser.NickName,
	}
	apis := make([]string, 0)
	apiList := make([]sysapi.SysApi, 0)
	for _, v := range model.SysApi {
		apis = append(apis, util.Int64ToString(v.Id))
		apiList = append(apiList, sysapi.SysApi{
			Id:     v.Id,
			Title:  v.Title,
			Path:   v.Path,
			Action: v.Action,
			Type:   v.Type,
			Handle: v.Handle,
		})
	}
	result.Apis = apis
	result.SysApi = apiList
	return result
}

func menuToMenuDto(model model.SysMenu, cmd menu.Menu) model.SysMenu {
	model.MenuId = cmd.MenuId
	model.MenuName = cmd.MenuName
	model.Title = cmd.Title
	model.Icon = cmd.Icon
	model.Path = cmd.Path
	model.Paths = cmd.Paths
	model.MenuType = cmd.MenuType
	model.Status = cmd.Status
	model.Action = cmd.Action
	model.Redirect = cmd.Redirect
	model.Permission = cmd.Permission
	model.ParentId = cmd.ParentId
	model.NoCache = cmd.NoCache
	model.Breadcrumb = cmd.Breadcrumb
	model.Component = cmd.Component
	model.Sort = cmd.Sort
	model.Visible = cmd.Visible
	model.IsFrame = cmd.IsFrame
	return model
}

// Remove 删除菜单内容
func (m sqlStore) Remove(ctx context.Context, cmd menu.RemoveMenuCommand) (err error) {
	var use int64
	tx := m.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	//菜单存在角色，请先删除
	err = tx.Table("sys_role_menu").Where("menu_id IN ? ", cmd.Ids).Count(&use).Error
	if err != nil {
		return err
	}
	if use > 0 {
		return menu.ErrMenuExistRole
	}
	var data model.SysMenu
	//菜单存在子菜单，请先删除
	err = tx.Table("sys_menu").Where("parent_id IN ? ", cmd.Ids).Count(&use).Error
	if err != nil {
		return err
	}
	if use > 0 {
		return menu.ErrMenuExitsChild
	}
	//菜单存在接口，先删除接口选择
	_ = tx.Where("sys_menu_menu_id IN ? ", cmd.Ids).Delete(&model.SysMenuAPI{}).Error
	err = tx.Model(&model.SysMenu{}).Delete(&data, cmd.Ids).Error
	if err != nil {
		return err
	}
	return
}

func ProvideStore(db *gorm.DB) sqlStore {
	return sqlStore{
		db:     db,
		logger: log.New("sysuser.store"),
	}
}

// GetMenuWithRole 首页登录后查询导航主菜单
func (m sqlStore) GetMenuWithRole(ctx context.Context, roleIds []string) (result []menu.Menu, err error) {
	var models []model.SysMenu
	if util.Contains(roleIds, "1828271398028574720") {
		err = m.db.Where("menu_type IN ('1','2') and status='1' ").Order("sort").Find(&models).Error
		for _, v := range models {
			result = append(result, menuDtoToMenu(v))
		}
		return
	}
	//基他用户角色
	var buttons []model.SysMenu
	var roleMenu []int64
	/**
	  实现思路：下面代码是先找出按钮->目录->菜单,按键不是应该此处查询
	*/
	// find menuType='F' button
	err = m.db.Table("sys_role_menu").Where("role_id IN ?", roleIds).Select("menu_id").Find(&roleMenu).Error
	if err != nil {
		return nil, err
	}
	err = m.db.Model(&model.SysMenu{}).Where("menu_type = '3' and menu_id in ?", roleMenu).Order("sort").Find(&buttons).Error

	//find parentId 目录
	mIds := make([]int64, 0)
	for _, menu := range buttons {
		if menu.ParentId != 0 {
			mIds = append(mIds, menu.ParentId)
		}
	}
	//menuType ='c' 目录
	var dataC []model.SysMenu
	err = m.db.Where("  menu_id in ? and  menu_type in ('2') and status='1' ", mIds).Order("sort").Find(&dataC).Error
	if err != nil {
		return nil, err
	}
	for _, datum := range dataC {
		item := menuDtoToMenu(datum)
		item.Auths = getButtonsAuths(datum.MenuId, buttons) //查找目录按钮
		result = append(result, item)
	}
	//search find parentId dto
	cIds := make([]int64, 0)
	for _, menu := range dataC {
		if menu.ParentId != 0 {
			cIds = append(cIds, menu.ParentId)
		}
	}
	//menuType='m'
	var dataM []model.SysMenu
	err = m.db.Where("menu_id in ? and  menu_type in ('1') and status='1' ", cIds).Order("sort").Find(&dataM).Error
	if err != nil {
		return nil, err
	}
	for _, datum := range dataM {
		result = append(result, menuDtoToMenu(datum))
	}
	return
}

func (m sqlStore) getMenuByRole(ctx context.Context, roleId int64) (menu []model.SysMenu, err error) {
	//查询逻辑：1、管理员查询所有菜单和按钮。2、角色查询权限菜单和按钮

	return
}
func getButtonsAuths(parentId int64, butMenu []model.SysMenu) []string {
	var buttons []string
	for _, menu := range butMenu {
		if menu.ParentId == parentId {
			buttons = append(buttons, menu.Permission)
		}
	}
	return buttons
}

func (m sqlStore) UpdateMenuApi(ctx context.Context, cmd menu.UpdateMenuApiCommand) (err error) {
	var apiList = make([]model.SysApi, 0)
	var model = model.SysMenu{}
	m.db.Preload("SysApi").First(&model, cmd.MenuId)
	m.db.Where("id in ?", cmd.Apis).Find(&apiList)

	model.UpdatedAt = time.Now()
	model.UpdateBy = cmd.UserId
	model.SysApi = apiList
	return m.db.Save(&model).Error
}
